/*
 * @Author: LVGRAPE
 * @Date: 2024-01-10 17:46:22
 * @LastEditTime: 2025-10-10 21:02:04
 * @LastEditors: LVGRAPE
 * @Description:
 * @FilePath: \ble_aromatherapy_diffuser\AromatherapyDiffuser\hardware\W25QXX\loadSbc.c
 * 要啥没啥，爱咋咋的
 */

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>

 /**NOTE 下位机一次最多接收32个字节！
  * 数据包格式为 CMD[2],FLASH_ADDR[4],DATA_SIZE[1],DATA[CHECK],DATA[24], 共32个字节
  * CMD[2]: 两个字节命令（相互取反）
  * FLASH_ADDR[4]: 4个字节flash地址
  * DATA_SIZE[1]: 1个字节数据长度
  * DATA_CHECK[1]: 1个字节数据检验
  * DATA[24]: 24个数据内容
  *
  * 每个包通信都会都有一个ACK表示成功
  */

  /**NOTE FLASH存储方式 SBC信息（大小与名称）+ SBC数据
   * SBC信息：共24个字节，1字节头'<' + 4字节SBC数据大小 + 1字节名称长度 + 17字节名称 + 1字节尾'>'
   *
   */
#define CMD_FLASH_HAND_SHAKE 0x1c
#define CMD_FLASH_ERASE_SECTOR 0x2c
#define CMD_FLASH_WRITE 0x3c
#define CMD_FLASH_WRITE_ADDR 0x4c
#define CMD_FLASH_WRITE_DATA 0x5c
#define CMD_FLASH_WRITE_INFO 0x6c
#define CMD_FLASH_READ_ADDR 0x7c
#define CMD_FLASH_READ_DATA 0x8c
#define CMD_FLASH_READ_INFO 0x9c
#define CMD_FLASH_EXIT 0xCC


#define CMD_FLASH_ACK 0x1F
#define CMD_FLASH_NACK 0xF1
#define CMD_FLASH_EOK 0X00

#define MSG_DATA_SIZE_MAX 256
#define MSG_PACK_SIZE (MSG_DATA_SIZE_MAX+9)//DATA+CMD[2]+ADDR[4]+CRC[1]+SIZE[2]

#define FLASH_SECTOR_SIZE 4096
#define FLASH_PAGE_SIZE 256
struct msg_pack {
    uint8_t cmd[2];
    uint8_t addr[4];
    uint16_t data_size;
    uint8_t data_check;
    uint8_t data[MSG_DATA_SIZE_MAX];
}__packed;
struct sbc_info {
    uint8_t head;
    uint32_t addr;
    uint32_t size;
    uint8_t name_len;
    uint8_t name[245];
    uint8_t tail;
}__packed;
static void msg_doCheck(struct msg_pack* msg) {
    msg->data_check = 0;
    for (int i = 0; i < msg->data_size; i++) {
        msg->data_check ^= msg->data[i];
    }
}
static int msg_Check(struct msg_pack* msg) {
    static uint8_t check = 0;
    for (int i = 0; i < msg->data_size; i++) {
        check ^= msg->data[i];
    }
    return (check == msg->data_check);
}
static int wait_ack(int fd)
{
    uint16_t tryCnt = 0;
    // usleep(10);//wait 5us
    while (1)
    {
        uint8_t ack;
        // tcflush(fd, TCIOFLUSH); //刷新输入输出缓冲区
        int rCnt = read(fd, &ack, 1);
        if (rCnt)
        {
            if (ack == CMD_FLASH_ACK) {
                // printf("get ack %02x\n", ack);
                return CMD_FLASH_EOK;
            }
            else
            {
                // printf("wait ack:0x%02x, %d/100, %d\n", ack, tryCnt, rCnt);
                usleep(1);//wait 5ms
            }
        }
        if (tryCnt++ > 1024) break;
    }
    return -1;
}
/**
 * @brief
 *
 * @param fd
 * @param msg
 * @return int 0:ok
 */
int send_msg_and_wait_ack(int fd, struct msg_pack* msg)
{
    uint8_t tryCnt = 0;
    while (1)
    {
        tcflush(fd, TCIOFLUSH);
        write(fd, msg->cmd, 2);
        write(fd, msg->addr, 4);
        write(fd, &(msg->data_size), 2);
        write(fd, &(msg->data_check), 1);
        write(fd, msg->data, msg->data_size);
        // tcflush(fd, TCIOFLUSH); //刷新输入输出缓冲区
        if (wait_ack(fd) == CMD_FLASH_EOK) return CMD_FLASH_EOK;
        // printf("send_msg_and_wait_ack ... cmd:%x,%x\n", msg->cmd[0], msg->cmd[1]);
        if (tryCnt++ > 3)
        {
            printf("send_msg_and_wait_ack error, no ack!\n");
            return -2;
        }
    }
    return -1;
}
/**
 * @brief
 *
 * @param argc 4
 * @param argv [com] [baudrate] [sbc_file] [writeAddr]
 * @return int
 */
int main(int argc, char* argv[])
{
    //ffmpeg -i ./wav/happy_new_year.wav ./wav/happyNewYear.sbc -ar 8000
    //"ls /dev/tty*"可扫描口列表
    //gcc ./AromatherapyDiffuser/hardware/W25QXX/loadSbc.c -o loadsbc
    printf("system run \n");

    if (argc < 6)
    {
        printf("Usage:%s [com] [baudrate] [sbc_file] [writeAddr] [writeSize]\n", argv[0]);
        printf("Example:\n ./loadsbc COM9 921600 ./wav/8k_good_luck_new_spring.sbc 0X00000000 0\n");
        printf("Example:\n ./loadsbc COM9 921600 ./wav/8k_happyNewYear.sbc 0X00200000 0\n");
        printf("Example:\n ./loadsbc COM9 921600 ./wav/8k_happyNewSpring.sbc 0X00400000 0\n");
        return -1;
    }
    char* com = argv[1];
    char linuxCom[12] = "/dev/ttyS";
    int comIndex = -1;
    uint32_t startAddr = strtoul(argv[4], NULL, 0);
    uint32_t writeSize = strtoul(argv[5], NULL, 0);
    if (strncmp(com, "COM", 3) == 0)
    {
        comIndex = atoi(com + 3) - 1;
        if (comIndex < 9)
            linuxCom[9] = comIndex + '0';
        else if (comIndex < 100)
        {
            linuxCom[9] = comIndex / 10 + '0';
            linuxCom[10] = comIndex % 10 + '0';
        }
        else
        {
            printf("com error!\n");
            return -1;
        }
        printf("comIndex = %d\n", comIndex);
        printf("com = %s\n", linuxCom);
    }
    else
    {
        printf("com error!\n");
        return -1;
    }
    int baudrate = atoi(argv[2]);
    printf("baudrate:%d \n", baudrate);

    char* sbc_file = argv[3];


    int f_com = open(linuxCom, O_RDWR | O_NOCTTY | O_NDELAY);
    if (f_com == -1)
    {
        printf("com port open error! %s\n", strerror(errno));
        return -1;
    }
    printf("serial init\n");
    struct termios options;
    tcgetattr(f_com, &options);
    switch (baudrate)
    {
    case 9600:
        cfsetispeed(&options, B9600); //设置输入波特率为921600
        cfsetospeed(&options, B9600); //设置输出波特率为921600
        break;
    case 115200:
        cfsetispeed(&options, B115200); //设置输入波特率为921600
        cfsetospeed(&options, B115200); //设置输出波特率为921600
        break;
    case 921600:
        cfsetispeed(&options, B921600); //设置输入波特率为921600
        cfsetospeed(&options, B921600); //设置输出波特率为921600
        break;

    default:

        cfsetispeed(&options, B921600); //设置输入波特率为921600
        cfsetospeed(&options, B921600); //设置输出波特率为921600
        break;
    }
    options.c_cflag |= (CLOCAL | CREAD); //忽略调制解调器控制线，启用接收器
    options.c_cflag &= ~PARENB; //无奇偶校验
    options.c_cflag &= ~CSTOPB; //1位停止位
    options.c_cflag &= ~CSIZE; //清除数据位设置
    options.c_cflag |= CS8; //设置数据位为8位
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //非规范模式，禁止
    options.c_oflag &= ~OPOST; //原始输出
    options.c_cc[VTIME] = 1; //设置超时1
    options.c_cc[VMIN] = 1; //设置最小字符为1
    tcsetattr(f_com, TCSANOW, &options);

    printf("serial set done\n");

    //判断文件尾缀是否为.sbc
    // if (strstr(sbc_file, ".sbc") == NULL)
    // {
    //     printf("sbc file error! Only accept *.sbc file!\n");
    //     return -1;
    // }

    FILE* f_sbc = fopen(sbc_file, "r");
    if (f_sbc == NULL)
    {
        printf("sbc file open error! %s\n", strerror(errno));
        return -1;
    }
    fseek(f_sbc, 0, SEEK_END);
    size_t total_size = ftell(f_sbc);
    total_size = writeSize ? writeSize : total_size;
    fseek(f_sbc, 0, SEEK_SET);
    printf("sbc file size = %d(0x%X)\n", total_size, total_size);
    char* sbcName = strrchr(sbc_file, '/') + 1;
    printf("sbc file name = %s\n", sbcName);
    struct sbc_info sbcInfo={0};
    sbcInfo.head = '<';
    sbcInfo.size = total_size;
    sbcInfo.addr = startAddr;
    sbcInfo.tail = '>';
    sbcInfo.name_len = strlen(sbcName) > sizeof(sbcInfo.name) ? sizeof(sbcInfo.name) : strlen(sbcName);
    strncpy(sbcInfo.name, sbcName, sbcInfo.name_len);
    printf("sbcInfo.name = %s %d\n", sbcInfo.name, sizeof(sbcInfo));
    printf("sbcInfo.name = %d\n", sbcInfo.size);
    printf("sbcInfo.addr = 0x%08X\n", sbcInfo.addr);

    //do handshake
    int handshake_try = 0;
    while (1)
    {
        uint8_t cmd[2];
        cmd[0] = CMD_FLASH_HAND_SHAKE;
        cmd[1] = ~CMD_FLASH_HAND_SHAKE;

        tcflush(f_com, TCIOFLUSH);
        printf("try handshake...%d/3\n", handshake_try);
        if (write(f_com, cmd, 2) != 2)
        {
            printf("write error! %s\n", strerror(errno));
            return -1;
        }

        if (wait_ack(f_com) == CMD_FLASH_EOK)
        {
            printf("handshake success!\n");
            break;
        }

        handshake_try++;
        if (handshake_try > 3)
        {
            printf("handshake fail!\n");
            return -1;
        }
    }

    size_t sector_size = 0;
    static struct msg_pack txPack;
    size_t read_size = 0;
    // total_size = 256 * 20;
    size_t total_write = 0;
    size_t total_pack = total_size / 256 + (total_size % 256 ? 1 : 0);
    size_t pack_count = 0;
    uint32_t write_addr = startAddr;
    uint32_t sector_count = 0;
    printf("pack_count:%d\n", pack_count);
    // time_t current_time = time(NULL);
    // struct tm *p_tm = localtime(&current_time);

    // timer_t start_time=time(NULL);
    // timer_t bootTime=time(NULL);
    // timer_t used_time;
    // timer_t totalTime;
    timer_t startTime = time(NULL);
    timer_t frameTime = time(NULL);
    timer_t frameTimeUsed = time(NULL);
    timer_t totalTime = time(NULL);
    timer_t totalTimeUsed = time(NULL);
    while (1)
    {
        //erase 4K rom
        frameTime = time(NULL);

        //NOTE erase sector
        if (pack_count == 0)
        {
            printf("* erase sector:0x%x...\n", write_addr);
            txPack.cmd[0] = CMD_FLASH_ERASE_SECTOR;
            txPack.cmd[1] = ~CMD_FLASH_ERASE_SECTOR;
            txPack.addr[0] = (uint8_t)(write_addr >> 24);
            txPack.addr[1] = (uint8_t)(write_addr >> 16);
            txPack.addr[2] = (uint8_t)(write_addr >> 8);
            txPack.addr[3] = (uint8_t)(write_addr);
            txPack.data_size = 0;
            msg_doCheck(&txPack);
            if (send_msg_and_wait_ack(f_com, &txPack) != CMD_FLASH_EOK)
            {
                printf("erase sector failed\n");
                return -1;
            }
            printf("erase sector success\n");
        }
        //first pack for sbc info
        if (pack_count == 0)
        {
            printf("* write sbc info...\n");

            txPack.cmd[0] = CMD_FLASH_WRITE_DATA;
            txPack.cmd[1] = ~CMD_FLASH_WRITE_DATA;
            txPack.addr[0] = (uint8_t)(write_addr >> 24);
            txPack.addr[1] = (uint8_t)(write_addr >> 16);
            txPack.addr[2] = (uint8_t)(write_addr >> 8);
            txPack.addr[3] = (uint8_t)(write_addr);
            memcpy(txPack.data, &sbcInfo, sizeof(sbcInfo));
            for (int i = 0;i < (MSG_DATA_SIZE_MAX);i++)
            {
                printf("%02X ", txPack.data[i]);
                if((i%32==31)&&(i>0))
                    printf("\n");
            }
            printf("\n");
            txPack.data_size = MSG_DATA_SIZE_MAX;
            msg_doCheck(&txPack);
            if (send_msg_and_wait_ack(f_com, &txPack) != CMD_FLASH_EOK)
            {
                printf("write sbc info failed\n");
                return -1;
            }


            printf("*%d cmd %x %x, 0x%02X%02X%02X%02X, size:0x%02X, ", pack_count, txPack.cmd[0], txPack.cmd[1], txPack.addr[0], txPack.addr[1], txPack.addr[2], txPack.addr[3], txPack.data_size);
            printf("addr = 0x%06X, this_write:%02d sector_size = %4d, total_size = %d/%d %0.2f%%\n", write_addr, read_size, sector_size, total_write, total_size, (float)(total_write * 100 / total_size));
            write_addr += MSG_DATA_SIZE_MAX;
            sector_size += MSG_DATA_SIZE_MAX;
            pack_count++;
        }
        if (sector_size % FLASH_SECTOR_SIZE == 0)
        {
            uint32_t sector_addr = startAddr + sector_count * FLASH_SECTOR_SIZE;
            sector_size = 0;
            printf("* erase sector:0x%x...\n", sector_addr);
            txPack.cmd[0] = CMD_FLASH_ERASE_SECTOR;
            txPack.cmd[1] = ~CMD_FLASH_ERASE_SECTOR;
            txPack.addr[0] = (uint8_t)(sector_addr >> 24);
            txPack.addr[1] = (uint8_t)(sector_addr >> 16);
            txPack.addr[2] = (uint8_t)(sector_addr >> 8);
            txPack.addr[3] = (uint8_t)(sector_addr);
            txPack.data_size = 0;
            msg_doCheck(&txPack);

            if (send_msg_and_wait_ack(f_com, &txPack) != CMD_FLASH_EOK)
            {
                printf("erase sector failed\n");
                return -1;
            }
        }

        size_t write_left = total_size - total_write;
        size_t thisRead = write_left > MSG_DATA_SIZE_MAX ? MSG_DATA_SIZE_MAX : write_left;
        if ((sector_size + thisRead) >= FLASH_SECTOR_SIZE)
        {
            thisRead = FLASH_SECTOR_SIZE - sector_size;

            sector_count++;
        }

        txPack.cmd[0] = CMD_FLASH_WRITE_DATA;
        txPack.cmd[1] = ~CMD_FLASH_WRITE_DATA;
        txPack.addr[0] = (write_addr >> 24);
        txPack.addr[1] = (write_addr >> 16);
        txPack.addr[2] = (write_addr >> 8);
        txPack.addr[3] = (write_addr);

        read_size = fread(txPack.data, 1, thisRead, f_sbc);
        txPack.data_size = read_size;
        msg_doCheck(&txPack);
        if (send_msg_and_wait_ack(f_com, &txPack) != CMD_FLASH_EOK)
        {
            printf("error in write data at %x\n", write_addr);
            return -1;
        }
        printf("*%d cmd %x %x, 0x%02X%02X%02X%02X, size:0x%02X, ", pack_count, txPack.cmd[0], txPack.cmd[1], txPack.addr[0], txPack.addr[1], txPack.addr[2], txPack.addr[3], txPack.data_size);
        printf("next_addr = 0x%06X, this_write:%02d, sector_size = %4d, total_size = %d/%d>%0.3f%%, ", write_addr, read_size, sector_size, total_write, total_size, (float)((float)total_write * 100.f / (float)total_size));
        // printf("T:%d:%d:%d/%d:%d:%d,%0.4fByte/s\n",pTimeUsed->tm_hour,pTimeUsed->tm_min,pTimeUsed->tm_sec,pTimeTotal->tm_hour,pTimeTotal->tm_min,pTimeTotal->tm_sec,speed);
        sector_size += read_size;
        total_write += read_size;
        write_addr += read_size;
        pack_count++;
        timer_t currentTime = time(NULL);
        frameTimeUsed = currentTime - frameTime;
        totalTimeUsed = currentTime - startTime;
        totalTime = totalTimeUsed * total_size / total_write; // 计算总时间
        size_t u_hour = totalTime / 3600;
        size_t u_min = (totalTime - u_hour * 3600) / 60;
        size_t u_sec = totalTime - u_hour * 3600 - u_min * 60;
        size_t h_hour = totalTimeUsed / 3600;
        size_t h_min = (totalTimeUsed - h_hour * 3600) / 60;
        size_t h_sec = totalTimeUsed - h_hour * 3600 - h_min * 60;
        printf("T:%d/%d, %02d:%02d:%02d/%02d:%02d:%02d\n", totalTimeUsed, totalTime, h_hour, h_min, h_sec, u_hour, u_min, u_sec);
        // struct tm *pTimeUsed = localtime(&totalTimeUsed);
        // struct tm *pTimeTotal = localtime(&totalTime);
        // float speed=read_size*1000000.0/frameTimeUsed;
        if (total_write >= total_size)
        {
            printf("total write done\n");
            break;
        }

    }


    printf("sbc_file = %s \n", sbc_file);
    printf("serial write\n");
    write(f_com, "hello world!\n", strlen("hello world!\n")); //发送数据
    printf("serial flush\n");
    tcflush(f_com, TCIOFLUSH); //刷新输入输出缓冲区

    close(f_com);



    return 0;
}
